"""
Map system for tank battle game
"""
import pygame
import random
from typing import List, Tuple, Optional
from utils.constants import TileType, Direction
from utils.config import WINDOW_WIDTH, WINDOW_HEIGHT, BROWN, GRAY, GREEN, BLUE


class MapTile:
    """Individual map tile"""
    
    def __init__(self, tile_type: int, x: int, y: int):
        """
        Initialize map tile
        
        Args:
            tile_type: Type of tile (TileType enum)
            x: X coordinate
            y: Y coordinate
        """
        self.tile_type = tile_type
        self.x = x
        self.y = y
        self.width = 32
        self.height = 32
        self.destroyed = False
        
        # Visual properties
        self.color = self._get_tile_color()
        self.rect = pygame.Rect(x, y, self.width, self.height)
        
        # Collision properties
        self.solid = self._is_solid()
        self.destructible = self._is_destructible()
    
    def _get_tile_color(self) -> Tuple[int, int, int]:
        """Get tile color based on type"""
        color_map = {
            TileType.EMPTY: (0, 0, 0),  # Black (transparent)
            TileType.BRICK: BROWN,
            TileType.STEEL: GRAY,
            TileType.GRASS: GREEN,
            TileType.WATER: BLUE
        }
        return color_map.get(self.tile_type, (0, 0, 0))
    
    def _is_solid(self) -> bool:
        """Check if tile is solid (blocks movement)"""
        # Only brick and steel block movement
        # Grass and water do NOT block movement
        solid_types = {TileType.BRICK, TileType.STEEL}
        return self.tile_type in solid_types and not self.destroyed
    
    def _is_destructible(self) -> bool:
        """Check if tile can be destroyed by bullets"""
        return self.tile_type == TileType.BRICK
    
    def take_damage(self, damage: int = 1, can_destroy_steel: bool = False) -> bool:
        """
        Take damage and return True if destroyed
        
        Args:
            damage: Amount of damage
            can_destroy_steel: Whether steel walls can be destroyed
            
        Returns:
            True if tile is destroyed
        """
        if self.destroyed:
            return False
            
        # 检查是否可以破坏该类型的瓦片
        if self.tile_type == TileType.BRICK:
            # 砖墙总是可以被破坏
            self.destroyed = True
            self.solid = False
            return True
        elif self.tile_type == TileType.STEEL and can_destroy_steel:
            # 钢墙只有在有道具时才能被破坏
            self.destroyed = True
            self.solid = False
            return True
            
        return False
    
    def render(self, screen: pygame.Surface):
        """Render tile on screen"""
        if self.tile_type == TileType.EMPTY or self.destroyed:
            return
        
        # Draw tile
        pygame.draw.rect(screen, self.color, self.rect)
        
        # Add tile-specific details
        self._render_tile_details(screen)
    
    def _render_tile_details(self, screen: pygame.Surface):
        """Render tile-specific details"""
        if self.tile_type == TileType.BRICK:
            # Draw brick pattern
            for i in range(0, self.width, 8):
                for j in range(0, self.height, 8):
                    if (i + j) % 16 == 0:
                        pygame.draw.rect(screen, (101, 67, 33), 
                                       (self.x + i, self.y + j, 4, 4))
        
        elif self.tile_type == TileType.STEEL:
            # Draw steel pattern
            pygame.draw.rect(screen, (200, 200, 200), 
                           (self.x + 2, self.y + 2, self.width - 4, self.height - 4))
            pygame.draw.rect(screen, (100, 100, 100), 
                           (self.x + 4, self.y + 4, self.width - 8, self.height - 8))
        
        elif self.tile_type == TileType.GRASS:
            # Draw grass pattern
            for i in range(0, self.width, 4):
                pygame.draw.line(screen, (0, 150, 0), 
                               (self.x + i, self.y), 
                               (self.x + i, self.y + self.height), 1)
        
        elif self.tile_type == TileType.WATER:
            # Draw water pattern
            for i in range(0, self.width, 8):
                pygame.draw.line(screen, (0, 100, 200), 
                               (self.x + i, self.y), 
                               (self.x + i, self.y + self.height), 2)


class GameMap:
    """Game map manager"""
    
    def __init__(self, width: int = WINDOW_WIDTH, height: int = WINDOW_HEIGHT):
        """
        Initialize game map
        
        Args:
            width: Map width in pixels
            height: Map height in pixels
        """
        self.width = width
        self.height = height
        self.tile_size = 32
        
        # Calculate grid dimensions
        self.grid_width = width // self.tile_size
        self.grid_height = height // self.tile_size
        
        # Initialize tile grid
        self.tiles: List[List[MapTile]] = []
        self._initialize_empty_map()
        
        # Map boundaries
        self.boundary_rect = pygame.Rect(0, 0, width, height)
    
    def _initialize_empty_map(self):
        """Initialize empty map"""
        self.tiles = []
        for y in range(self.grid_height):
            row = []
            for x in range(self.grid_width):
                tile = MapTile(TileType.EMPTY, x * self.tile_size, y * self.tile_size)
                row.append(tile)
            self.tiles.append(row)
    
    def generate_random_map(self, brick_density: float = 0.3, 
                           steel_density: float = 0.1):
        """
        Generate random map
        
        Args:
            brick_density: Density of brick tiles (0.0 to 1.0)
            steel_density: Density of steel tiles (0.0 to 1.0)
        """
        for y in range(self.grid_height):
            for x in range(self.grid_width):
                # Set tile type based on position
                if (x == 0 or x == self.grid_width - 1 or 
                    y == 0 or y == self.grid_height - 1):
                    # Boundary walls - always steel
                    tile_type = TileType.STEEL
                else:
                    # Random tile generation for interior
                    rand = random.random()
                    if rand < steel_density:
                        tile_type = TileType.STEEL
                    elif rand < steel_density + brick_density:
                        tile_type = TileType.BRICK
                    else:
                        tile_type = TileType.EMPTY
                
                # Update all tile properties
                tile = self.tiles[y][x]
                tile.tile_type = tile_type
                tile.destroyed = False  # Reset destroyed state
                tile.color = tile._get_tile_color()
                tile.solid = tile._is_solid()
                tile.destructible = tile._is_destructible()
    
    def load_map_from_data(self, map_data: List[List[int]]):
        """
        Load map from 2D array data
        
        Args:
            map_data: 2D array of tile types
        """
        for y, row in enumerate(map_data):
            if y >= self.grid_height:
                break
            for x, tile_type in enumerate(row):
                if x >= self.grid_width:
                    break
                self.tiles[y][x].tile_type = tile_type
                self.tiles[y][x].color = self.tiles[y][x]._get_tile_color()
                self.tiles[y][x].solid = self.tiles[y][x]._is_solid()
                self.tiles[y][x].destructible = self.tiles[y][x]._is_destructible()
    
    def get_tile_at_position(self, x: int, y: int) -> Optional[MapTile]:
        """
        Get tile at world position
        
        Args:
            x: World X coordinate
            y: World Y coordinate
            
        Returns:
            MapTile at position or None
        """
        grid_x = x // self.tile_size
        grid_y = y // self.tile_size
        
        if (0 <= grid_x < self.grid_width and 
            0 <= grid_y < self.grid_height):
            return self.tiles[grid_y][grid_x]
        return None
    
    def get_tiles_in_rect(self, rect: pygame.Rect) -> List[MapTile]:
        """
        Get all tiles intersecting with rectangle
        
        Args:
            rect: Rectangle to check
            
        Returns:
            List of intersecting tiles
        """
        tiles = []
        
        # Calculate grid bounds
        start_x = max(0, rect.left // self.tile_size)
        end_x = min(self.grid_width, (rect.right + self.tile_size - 1) // self.tile_size)
        start_y = max(0, rect.top // self.tile_size)
        end_y = min(self.grid_height, (rect.bottom + self.tile_size - 1) // self.tile_size)
        
        for y in range(start_y, end_y):
            for x in range(start_x, end_x):
                tile = self.tiles[y][x]
                if tile.rect.colliderect(rect):
                    tiles.append(tile)
        
        return tiles
    
    def check_collision(self, rect: pygame.Rect) -> bool:
        """
        Check collision with solid tiles
        
        Args:
            rect: Rectangle to check collision with
            
        Returns:
            True if collision detected
        """
        tiles = self.get_tiles_in_rect(rect)
        return any(tile.solid for tile in tiles)
    
    def damage_tiles_in_rect(self, rect: pygame.Rect, damage: int = 1, can_destroy_steel: bool = False) -> List[MapTile]:
        """
        Damage destructible tiles in rectangle
        
        Args:
            rect: Rectangle to damage
            damage: Amount of damage
            can_destroy_steel: Whether steel walls can be destroyed
            
        Returns:
            List of destroyed tiles
        """
        destroyed_tiles = []
        tiles = self.get_tiles_in_rect(rect)
        
        for tile in tiles:
            if tile.take_damage(damage, can_destroy_steel):
                destroyed_tiles.append(tile)
        
        return destroyed_tiles
    
    def render(self, screen: pygame.Surface):
        """Render entire map"""
        for row in self.tiles:
            for tile in row:
                tile.render(screen)
    
    def render_debug(self, screen: pygame.Surface):
        """Render map with collision debug overlay"""
        for row in self.tiles:
            for tile in row:
                tile.render(screen)
                
                # Draw collision boxes for solid tiles
                if tile.solid and not tile.destroyed:
                    pygame.draw.rect(screen, (255, 0, 0), tile.rect, 2)  # Red border
                    # Draw "X" to show it's solid
                    pygame.draw.line(screen, (255, 0, 0), 
                                   (tile.rect.left, tile.rect.top),
                                   (tile.rect.right, tile.rect.bottom), 1)
                    pygame.draw.line(screen, (255, 0, 0),
                                   (tile.rect.right, tile.rect.top),
                                   (tile.rect.left, tile.rect.bottom), 1)
    
    def get_spawn_positions(self, count: int) -> List[Tuple[int, int]]:
        """
        Get valid spawn positions for tanks
        
        Args:
            count: Number of positions needed
            
        Returns:
            List of (x, y) spawn positions
        """
        positions = []
        attempts = 0
        max_attempts = 1000
        
        while len(positions) < count and attempts < max_attempts:
            attempts += 1
            
            # Random position
            x = random.randint(1, self.grid_width - 2) * self.tile_size
            y = random.randint(1, self.grid_height - 2) * self.tile_size
            
            # Check if position is valid (empty and not too close to others)
            rect = pygame.Rect(x, y, 32, 32)
            if not self.check_collision(rect):
                # Check distance from existing positions
                too_close = False
                for pos in positions:
                    distance = ((x - pos[0]) ** 2 + (y - pos[1]) ** 2) ** 0.5
                    if distance < 100:  # Minimum distance
                        too_close = True
                        break
                
                if not too_close:
                    positions.append((x, y))
        
        return positions
    
    def validate_and_fix_tiles(self):
        """
        Validate and fix tile properties to ensure consistency.
        Fixes invisible collision bugs.
        """
        fixed_count = 0
        for row in self.tiles:
            for tile in row:
                # Recalculate properties from tile_type
                old_solid = tile.solid
                tile.solid = tile._is_solid()
                tile.destructible = tile._is_destructible()
                tile.color = tile._get_tile_color()
                
                # If destroyed, ensure not solid
                if tile.destroyed:
                    tile.solid = False
                
                if old_solid != tile.solid:
                    fixed_count += 1
        
        if fixed_count > 0:
            print(f"Fixed {fixed_count} tile collision inconsistencies")
        
        return fixed_count